package com.fighting;

import org.activiti.engine.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;

/**
 * @author ：HBZ
 * @date ：Created in 2020/12/23 14:50
 * @description：
 * @modified By：
 */

public class ActiUtil {
    RuntimeService runtimeService;
    RepositoryService repositoryService;
    TaskService taskService;
    HistoryService historyService;

    public void setRuntimeService(RuntimeService runtimeService) {
        this.runtimeService = runtimeService;
    }

    public void setRepositoryService(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    public void setTaskService(TaskService taskService) {
        this.taskService = taskService;
    }

    public void setHistoryService(HistoryService historyService) {
        this.historyService = historyService;
    }

    /**
     * 撤回，第一种方式：
     * 1、获取当前节点，获取跳转节点
     * 2、获取节点的所有流出流向，把流向保存到临时变量oldPvmTransitionList
     * 3、清空流向，即删除流向
     * 4、新增流向，将流向的Destination设置成跳转节点
     * 5、领取任务，完成任务
     * 6、删除当前节点的流向，将流向还原成临时变量oldPvmTransitionList中的值
     *
     * @param taskId      当前任务id
     * @param jumpTaskKey 要跳转的节点的key
     * @param assignee    执行撤回任务的用户
     */
    public void withdraw1(String taskId, String jumpTaskKey, String assignee) {
        Task task = findTaskById(taskId);
        if (task.getAssignee() != null && !assignee.equals(task.getAssignee())) {
            throw new ActivitiException("任务已被别人领取，不允许撤回");
        }
        ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);
        // 当前节点
        ActivityImpl currActivity = processDefinition.findActivity(task.getTaskDefinitionKey());
        // 跳转节点
        ActivityImpl jumpActivity = processDefinition.findActivity(jumpTaskKey);
        // 清空当前节点的输出流向
        List<PvmTransition> oldPvmTransitionList = clearTransition(currActivity);
        // 创建新流向,即将当前节点的流向指向需要跳转的节点流向
        TransitionImpl newTransition = currActivity.createOutgoingTransition();
        // 设置新流向的目标节点，将当前节点指向需要跳转的节点
        newTransition.setDestination(jumpActivity);
        taskService.claim(taskId, assignee);
        taskService.complete(taskId);
        restoreTransition(currActivity, oldPvmTransitionList);
    }

    /**
     * 撤回，第二种方式：当前流程输出流向的destination指向要跳转的节点，任务执行完再重新指向原节点
     * 前提：当前节点只有一个流出节点，如果有多个流出节点可能会跳转到其他节点
     * 1、获取当前节点，获取跳转节点
     * 2、获取节点的第一个流向（前提说明：只会有一个流向），把流向保存到临时变量oldActivity
     * 3、将流向的Destination设置成跳转节点
     * 4、领取任务，完成任务
     * 5、将流向的Destination设置成临时变量oldActivity
     *
     * @param taskId      当前任务id
     * @param jumpTaskKey 要跳转的节点的key
     * @param assignee    执行撤回任务的用户
     */
    void withdraw2(String taskId, String jumpTaskKey, String assignee) {
        Task task = findTaskById(taskId);
        if (task.getAssignee() != null && !assignee.equals(task.getAssignee())) {
            throw new ActivitiException("任务已被别人领取，不允许撤回");
        }
        ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);
        // 当前节点
        ActivityImpl currActivity = processDefinition.findActivity(task.getTaskDefinitionKey());
        // 跳转节点
        ActivityImpl preActivity = processDefinition.findActivity(jumpTaskKey);
        // 当前节点的输出流向
        List outTransitions = currActivity.getOutgoingTransitions();
        if (outTransitions.size() == 0) {
            throw new ActivitiException("流程错误");
        }
        // 获取第一条流向
        TransitionImpl transition = (TransitionImpl) outTransitions.get(0);
        // 获取原流程的下一个节点
        ActivityImpl oldActivity = transition.getDestination();
        // 流向指向前一个节点
        transition.setDestination(preActivity);
        taskService.claim(taskId, assignee);
        // 执行流程
        taskService.complete(taskId);
        // 还原成原节点
        transition.setDestination(oldActivity);
    }

    /**
     * 根据任务ID获取流程定义
     *
     * @param taskId 任务ID
     * @return 流程定义实体*
     */
    public ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(String taskId) {
        // 取得流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(findTaskById(taskId).getProcessDefinitionId());
        if (processDefinition == null) {
            throw new ActivitiException("流程定义未找到!");
        }
        return processDefinition;
    }

    /**
     * 根据任务ID获得任务实例
     *
     * @param taskId 任务ID
     * @return 任务实例
     */
    Task findTaskById(String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            throw new ActivitiException("任务不存在!");
        }
        return task;
    }

    /**
     * 清空指定活动节点流向
     *
     * @param activityImpl 活动节点
     * @return 节点流向集合
     */
    private List<PvmTransition> clearTransition(ActivityImpl activityImpl) {
        // 存储当前节点所有流向临时变量
        List<PvmTransition> oldPvmTransitionList = new ArrayList<>();
        // 获取当前节点所有流向，存储到临时变量，然后清空
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        for (PvmTransition pvmTransition : pvmTransitionList) {
            oldPvmTransitionList.add(pvmTransition);
        }
        pvmTransitionList.clear();
        return oldPvmTransitionList;
    }

    /**
     * 还原指定活动节点流向
     *
     * @param activityImpl         活动节点
     * @param oldPvmTransitionList 原有节点流向集合
     */
    private void restoreTransition(ActivityImpl activityImpl, List<PvmTransition> oldPvmTransitionList) {
        // 清空现有流向
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        pvmTransitionList.clear();
        // 还原以前流向
        for (PvmTransition pvmTransition : oldPvmTransitionList) {
            pvmTransitionList.add(pvmTransition);
        }
    }
}
